package com.cwz.quartz.utils;

import com.cwz.api.entity.SysJob;
import com.cwz.core.constant.Constants;
import com.cwz.core.constant.ScheduleConstants;
import com.cwz.core.utils.spring.SpringUtils;
import com.cwz.core.utils.string.StringUtils;
import com.cwz.core.utils.task.TaskException;
import org.quartz.*;

/**
 * @program: w-demo
 * @description: 定时任务调度工具类
 * @author: Wen
 **/
public class ScheduleUtils {
	/**
	 * 得到quartz任务类
	 *
	 * @param sysJob 执行计划
	 * @return 具体执行任务类
	 */
	private static Class<? extends Job> getQuartzJobClass(SysJob sysJob) {
		boolean isConcurrent = "0".equals(sysJob.getConcurrent());
		return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentJobExecution.class;
	}

	/**
	 * 构建任务键对象
	 */
	public static JobKey getJobKey(Long jobId, String jobGroup) {
		return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
	}

	/**
	 * 构建任务触发对象
	 */
	public static TriggerKey getTriggerKey(Long jobId, String jobGroup) {
		return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
	}

	/**
	 * 创建定时任务
	 */
	public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException {
		Class<? extends Job> jobClass = getQuartzJobClass(job);
		// 构建job信息
		Long jobId = job.getJobId();
		String jobGroup = job.getJobGroup();
		JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();

		// 表达式调度构建器
		CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
		cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);

		// 按新的cronExpression表达式构建一个新的trigger
		CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup))
			.withSchedule(cronScheduleBuilder).build();

		// 放入参数，运行时的方法可以获取
		jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);

		// 判断是否存在
		if (scheduler.checkExists(getJobKey(jobId, jobGroup))) {
			// 防止创建时存在数据问题 先移除，然后在执行创建操作
			scheduler.deleteJob(getJobKey(jobId, jobGroup));
		}

		// 判断任务是否过期
		if (StringUtils.isNotNull(CronUtils.getNextExecution(job.getCronExpression()))) {
			// 执行调度任务
			scheduler.scheduleJob(jobDetail, trigger);
		}

		// 暂停任务
		if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) {
			scheduler.pauseJob(getJobKey(jobId, jobGroup));
		}
	}

	/**
	 * 设置定时任务策略
	 */
	public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb) throws TaskException {
		switch (job.getMisfirePolicy()) {
			case ScheduleConstants.MISFIRE_DEFAULT:
				return cb;
			case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
				// Quartz不会判断发生了misfire，立即执行所有发生了misfire的任务，然后按照原计划进行执行。
				// 例如: 10:15分立即执行9:00和10:00的任务，然后等待下一个任务 在11:00执行，后续按照原计划执行。
				return cb.withMisfireHandlingInstructionIgnoreMisfires();
			case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
				// 立即执行第一个发生misfire的任务，忽略其他发生misfire的任务，然后按照原计划继续执行。
				// 例如: 在10:15立即执行9:00任务，忽略10:00任务，然后等待下一个任务在11:00执行，后续按照原计划执行。
				return cb.withMisfireHandlingInstructionFireAndProceed();
			case ScheduleConstants.MISFIRE_DO_NOTHING:
				// 所有发生misfire的任务都被忽略，只是按照原计划继续执行
				return cb.withMisfireHandlingInstructionDoNothing();
			default:
				throw new TaskException("The task misfire policy '" + job.getMisfirePolicy()
					+ "' cannot be used in cron schedule tasks", TaskException.Code.CONFIG_ERROR);
		}
	}

	/**
	 * 检查包名是否为白名单配置
	 *
	 * @param invokeTarget 目标字符串
	 * @return 结果
	 */
	public static boolean whiteList(String invokeTarget) {
		String packageName = StringUtils.substringBefore(invokeTarget, "(");
		int count = StringUtils.countMatches(packageName, ".");
		if (count > 1) {
			return StringUtils.containsAnyIgnoreCase(invokeTarget, Constants.JOB_WHITELIST_STR);
		}
		Object obj = SpringUtils.getBean(StringUtils.split(invokeTarget, ".")[0]);
		return StringUtils.containsAnyIgnoreCase(obj.getClass().getPackage().getName(), Constants.JOB_WHITELIST_STR);
	}
}
